﻿using System;
using System.Drawing;
using System.Windows.Forms;


namespace CSettingsUI
{
	/// <summary>
	/// Grid for Controls with labels.
	/// Depending on Anchor property each Control can be stretched horizontaly.
	/// No vertical stretching is performed.
	/// Name property is used for labeling.
	/// </summary>
	public class FlowManager
	{	
		#region members	
		/// <summary>Control to manage</summary>
		private Control control;
		/// <summary>Spacing between the Controls</summary>
		private int spacing=5;
		/// <summary>Margin (Padding)</summary>
		private int margin=5;	
		/// <summary>Label length, the greatest label width counts</summary>
		private int currentLabelLength;
		/// <summary>Maximum Label Width before the Mangager breaks the line</summary>
		private int maximumLabelWidth= 200;
		#endregion

		public FlowManager(Control ctl)
		{
			this.control = ctl;						
			this.control.Paint +=new PaintEventHandler(Control_Paint);
			this.control.Layout +=new LayoutEventHandler(Control_Layout);
		
		}	


		#region public
		/// <summary>
		/// Sets or gets the maximum label width. If a label is larger, it will drawn multi line.
		/// </summary>
		public int MaximumLabelWidth
		{
			get
			{
				return this.maximumLabelWidth;
			}
			set
			{
				maximumLabelWidth = value;
			}
		}
		/// <summary>
		/// Margin (Padding) used for Top/Left
		/// </summary>
		public int Margin
		{
			get
			{
				return this.margin;
			}
			set
			{
				margin = value;
			}
		}
		/// <summary>
		/// Font used to draw the label text
		/// </summary>
		public Font Font
		{
			get
			{
				return control.Font;
			}
		}

		#endregion

		#region events
		/// <summary>
		/// Paints the Label Text
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Control_Paint(object sender, PaintEventArgs e)
		{
		
			using(Graphics graphics = this.control.CreateGraphics())
			{
				// draw labels
				Brush brush = new SolidBrush(this.control.ForeColor);
				foreach (Control control in this.control.Controls)
				{
					if (!control.Visible) continue;
			
					if(((int) graphics.MeasureString(control.Name, this.Font).Width > (maximumLabelWidth+ spacing)) )
					{
						RectangleF rectF = new RectangleF(new PointF(margin, control.Top + spacing), new SizeF(currentLabelLength,control.ClientRectangle.Height));
						Rectangle rect = new Rectangle(new Point(margin, control.Top + spacing), new Size(currentLabelLength,control.ClientRectangle.Height));
						//
						e.Graphics.DrawString(control.Name, this.Font, brush, rect, StringFormat.GenericDefault);
						e.Graphics.DrawRectangle(Pens.Orange,rect);					
					} 
					else
					{
						e.Graphics.DrawString(control.Name, this.Font, brush, margin, control.Top + spacing);
					}
				}
			}
		}

		/// <summary>
		/// Checks each control's label text, get the most valid width, and rearrange the controls.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Control_Layout(object sender, LayoutEventArgs e)
		{
			if (this.control.Controls.Count <= 0) return;
			// suspend for moving controls around da world
			this.control.SuspendLayout();
			// left position starts at margin
			int left = margin;
			//
			Graphics graphics = this.control.CreateGraphics();
			// check each control for his label width, if label width is greater than maximumLabelWidth throw label width cause
			// label will be drawn in a rectangle. Otherwise set current label width as greatest.
			foreach (Control control in this.control.Controls)
			{
				if (control.Visible)
				{
					int measureString = (int) graphics.MeasureString(control.Name, this.Font).Width + margin;
					left = (int)Math.Max((measureString >= (maximumLabelWidth+ spacing)) ? left: measureString, left);
				}
			}
			graphics.Dispose();
			// if there was no label width that was smaller than maximumLabelWidth, we use maximumLabelWidth
			if(left == margin)
			{
				left = maximumLabelWidth;
			}
			// set current label width for drawing (Paint Event)
			currentLabelLength = left - spacing;
			// 
			left += spacing;		
			// layout
			int top =margin;
			int maxControlWidth = maximumLabelWidth;
			foreach (Control control in this.control.Controls)
			{
				if (!control.Visible) continue;

				// top
				control.Top = top;
				top = control.Bottom + spacing;
				maxControlWidth = (int)Math.Max(control.Width, maxControlWidth);
				// left & right
				if ((control.Anchor & AnchorStyles.Left) != 0)
				{
					if ((control.Anchor & AnchorStyles.Right) != 0)
					{
						control.Width = this.control.ClientSize.Width - (left + margin);
					}				
					control.Left = left;
				}
				else
					control.Left = Math.Max(this.control.ClientSize.Width - control.Width+margin, left);
			}
			// Make same width
			foreach(Control c in this.control.Controls)
			{
				c.Width = maxControlWidth;
			}		
			this.control.ResumeLayout();
		}
		#endregion
	} 
}